<?php

namespace yunj\app\admin\service\member\log;

use think\facade\Db;
use yunj\app\admin\enum\auth\AuthPageOpen;
use yunj\app\admin\enum\auth\AuthType;
use yunj\app\admin\enum\RedisKey;
use yunj\app\admin\enum\route\RequestMethod;
use yunj\app\admin\enum\TableBuilderEvent;
use yunj\app\admin\job\AdminMemberLogRecordJob;
use yunj\app\admin\service\route\RouteRequestService;
use yunj\core\builder\YunjTable;
use yunj\core\Config;
use yunj\core\constants\AreaConst;
use yunj\core\constants\BuilderConst;

class LogQueryService extends Service {

    const PARENT_AUTH_KEY = 'yunj_member_log';

    /**
     * 类型
     * @var string
     */
    private $type;

    /**
     * 类型配置
     * @var array
     */
    private $typeConfig;

    public function __construct() {
        $type = request()->get('type');
        $typesConfig = self::getAllTypesConfig();
        if (!array_key_exists($type, $typesConfig)) {
            throw_error_json('异常访问');
        }
        $this->type = $type;
        $this->typeConfig = $typesConfig[$type];
    }

    // 获取所有的types配置
    public static function getAllTypesConfig(): array {
        $groups = Config::get('log.types', []) + yunj_config('log.types', []);
        return $groups;
    }

    /**
     * 权限key
     * @param string $type
     * @param string $sufix
     * @return string
     */
    public static function authKey(string $type = '', string ...$sufix): string {
        $key = self::PARENT_AUTH_KEY;
        if ($type) {
            $key .= '_' . $type;
        }
        foreach ($sufix as $v) {
            if ($v) {
                $key .= '_' . $v;
            }
        }
        return $key;
    }

    /**
     * 所有的权限
     * @return array[]
     */
    public static function auths() {
        $parentAuthKey = self::authKey();
        $auths = [
            $parentAuthKey => [
                'name' => '日志',
                'icon' => 'layui-icon-form',
                'parent' => 'yunj_admin_manage',
                'type' => AuthType::SIDEBAR_MENU,
            ],
        ];
        $types = self::getAllTypesConfig();
        foreach ($types as $type => $typeConfig) {
            $authKey = self::authKey($type);
            $authName = $typeConfig['title'] ?? '';
            $authParent = $typeConfig['parent'] ?? $parentAuthKey;
            $authUrl = build_url('yunjMemberLog');
            // 侧边栏菜单
            $auths[$authKey] = [
                'name' => $authName,
                'icon' => $typeConfig['icon'] ?? '',
                'parent' => $parentAuthKey,
                'type' => AuthType::SIDEBAR_MENU,
                'page_open' => AuthPageOpen::TAB,
                'url' => [$authUrl, 'get', ['type' => $type]],
            ];
            // 查看
            $viewAuthKey = $authKey . '_view';
            $auths[$viewAuthKey] = [
                'name' => '查看',
                'parent' => $authKey,
                'url' => [$authUrl, 'post', ['type' => $type]],
            ];
            // 字段
            $viewFieldAuthKey = $viewAuthKey . '_normal';
            $auths[$viewFieldAuthKey] = [
                'name' => '普通字段',
                'parent' => $viewAuthKey,
            ];
            // 操作
            $actionAuthKey = $authKey . '_action';
            $auths[$actionAuthKey] = [
                'name' => '操作',
                'parent' => $authKey,
            ];
            // 导出
            $actionExportAuthKey = $actionAuthKey . '_export';
            $auths[$actionExportAuthKey] = [
                'name' => '导出',
                'parent' => $actionAuthKey,
                'url' => [$authUrl, 'post', ['type' => $type, BuilderConst::ASYNC_TYPE_KEY => 'export']],
            ];
        }
        //dump($auths);
        return $auths;
    }

    /**
     * 所有的路由请求项
     * @return array[]
     */
    public static function requestItems() {
        $requestItems = [];
        $types = self::getAllTypesConfig();
        foreach ($types as $type => $typeConfig) {
            $requestItems = array_merge($requestItems, [
                [
                    'system_key' => md5('log_page_' . $type),
                    'type' => 'request',
                    'desc' => $typeConfig['title'] . '列表页面',
                    'method' => RequestMethod::GET,
                    'require_params' => [
                        'type' => $type
                    ],
                ],
                [
                    'system_key' => md5('log_data_get_' . $type),
                    'type' => 'request',
                    'desc' => $typeConfig['title'] . '列表数据获取',
                    'method' => RequestMethod::POST,
                    'require_params' => [
                        'type' => $type
                    ],
                ],
                [
                    'system_key' => md5('log_data_export_' . $type),
                    'type' => 'request',
                    'desc' => $typeConfig['title'] . '列表数据导出',
                    'method' => RequestMethod::POST,
                    'require_params' => [
                        'type' => $type,
                        BuilderConst::ASYNC_TYPE_KEY => 'export'
                    ],
                ]
            ]);
        }
        return $requestItems;
    }

    /**
     * 获取列表构建器
     * @return YunjTable
     */
    public function getListBuilder(): YunjTable {
        $args = [
            'filter' => function (YunjTable $builder, $state) {
                $ipProvinceOptions = [
                    'all' => '所有',
                    'other' => '其它',
                ];
                $provinceOptions = array_combine(AreaConst::PROVINCES, AreaConst::PROVINCES);
                array_insert($ipProvinceOptions, $provinceOptions, 'other');
                return [
                    'timeRange' => [
                        'title' => '时间范围',
                        'type' => 'date',
                        'max' => date('Y-m-d'),
                        'range' => true,
                        'verify' => 'require',
                        'default' => ['start' => date('Y-m') . '-01', 'end' => date('Y-m-d')],
                    ],
                    'memberIds' => [
                        'title' => '管理员',
                        'type' => 'dropdownSearch',
                        'options' => build_url('yunjAdminMemberDropdownsearch')
                    ],
                    'dataId' => [
                        'title' => '数据ID',
                    ],
                    'ipProvince' => [
                        'title' => 'IP所在地',
                        'type' => 'select',
                        'options' => $ipProvinceOptions,
                        'default' => 'all'
                    ]
                ];
            },
            'defaultToolbar' => [
                'filter',
                'export' => [
                    'auth' => self::authKey($this->type, 'action_export')
                ]
            ],
            'cols' => function (YunjTable $builder, $state) {
                $cols = [
                    'create_time' => ['title' => '时间', 'align' => 'center', 'templet' => 'datetime'],
                    'url' => ['title' => 'URL'],
                    'desc' => ['title' => '描述'],
                    'member_name' => ['title' => '管理员'],
                    'data_id' => ['title' => '数据ID'],
                    'request_params' => ['title' => '请求参数', 'templet' => 'json'],
                    'response_content' => ['title' => '响应内容', 'templet' => 'json'],
                    'ip_province' => ['title' => 'IP所在地'],
                    'device_info' => ['title' => '设备信息'],
                ];
                $this->setListBuilderColsAuth($cols);
                return $cols;
            },
            'count' => function (YunjTable $builder, array $filter) {
                $where = $this->getListBuilderFilterWhere($filter);
                $tableSuffixArr = self::getQueryYearMonthSuffixArr($filter['timeRange']);
                return self::getAdminMemberLogModel()->getUnionAllCount($tableSuffixArr, $where);
            },
            'items' => function (YunjTable $builder, int $page, int $pageSize, array $filter, array $sort) {
                $where = $this->getListBuilderFilterWhere($filter);
                $order = $sort + ['create_time' => 'desc'];
                $field = ['id', 'request_id', 'method', 'member_id', 'data_id', 'ip_province', 'platform', 'platform_version', 'browser', 'browser_version', 'create_time'];

                $tableSuffixArr = self::getQueryYearMonthSuffixArr($filter['timeRange']);
                $items = self::getAdminMemberLogModel()->getUnionAllRowsToArray($tableSuffixArr, $where, $field, $order, $page, $pageSize);
                $this->listItemsHandle($items, $tableSuffixArr);
                return $items;
            },
        ];
        return YT("yunj_member_{$this->type}_log_list", $args);
    }

    /**
     * 列表数据处理
     * @param array $items
     */
    private function listItemsHandle(array &$items, array $tableSuffixArr): void {
        if (!$items) return;
        $memberIds = array_column($items, 'member_id');
        $memberArr = self::getAdminMemberModel()->getOwnRowsToArray([['id', 'in', $memberIds]], ['id', 'username', 'name']);
        $memberNameArr = [];
        foreach ($memberArr as $v) {
            $memberNameArr[$v['id']] = "{$v['name']}【{$v['username']}】";
        }
        // log_attr
        $logIds = array_column($items, 'id');
        $logAttrs = self::getAdminMemberLogAttrModel()->getUnionAllRowsToArray($tableSuffixArr, [
            ['log_id', 'in', $logIds]
        ], ['log_id', 'query_string', 'request_params', 'response_content', 'desc']);
        if ($logAttrs) {
            $logAttrs = array_column($logAttrs, null, 'log_id');
        }
        foreach ($items as &$item) {
            $logId = $item['id'];
            // 属性信息
            $item += $logAttrs[$logId] ?? [
                    'log_id' => $logId,
                    'user_agent' => '',
                    'query_string' => '',
                    'request_params' => '',
                    'response_content' => '',
                    'desc' => ''
                ];
            // member_name
            $item['member_name'] = $memberNameArr[$item['member_id']] ?? '';
            // ip_province
            $item['ip_province'] = $item['ip_province'] ?: '未知';
            // url
            $adminRequestDbData = RouteRequestService::getValidRouteRequestDataById($item['request_id']);
            $baseUrl = $adminRequestDbData['route_base_url'];
            $url = $baseUrl;
            if ($url) {
                $url = "【{$item['method']}】{$url}";
                if ($item['query_string']) {
                    $url .= '?' . $item['query_string'];
                }
            }
            $item['url'] = $url;
            // desc 描述为空则读取配置的请求描述
            if (!$item['desc']) {
                $method = $item['method'];
                $requestParams = [];
                if ($item['request_params']) {
                    $requestParams = json_decode($item['request_params'], true);
                }
                $item['desc'] = $adminRequestDbData['full_desc'];
            }
            // device_info
            $item['platform'] = $item['platform'] ?: '未知';
            $item['platform_version'] = $item['platform_version'] ?: '未知';
            $item['browser'] = $item['browser'] ?: '未知';
            $item['browser_version'] = $item['browser_version'] ?: '未知';
            $deviceInfo = '';
            if ($item['platform']) {
                $deviceInfo .= $item['platform'];
                if ($item['platform_version']) {
                    $deviceInfo .= "（{$item['platform_version']}）";
                }
            }
            if ($item['browser']) {
                $deviceInfo .= ($deviceInfo ? '、' : '') . $item['browser'];
                if ($item['browser_version']) {
                    $deviceInfo .= "（{$item['browser_version']}）";
                }
            }
            $item['device_info'] = $deviceInfo;
        }
    }

    /**
     * 获取查询年月后缀数据
     * @param string $timeRange 时间范围。如：['start'=>'2023-01-01','end'=>'2023-02-02'] 返回：['202301','202302']
     * @return array
     */
    private static function getQueryYearMonthSuffixArr($timeRange): array {
        static $arr;
        $arr = $arr ?? [];

        $startTime = $timeRange['start'] ?? '';
        $endTime = $timeRange['end'] ?? '';
        $startTimestamp = strtotime($startTime);
        $endTimestamp = strtotime($endTime);
        if ($startTimestamp > $endTimestamp) {
            throw_error_json('查询开始时间不能大于查询截至时间');
        }

        $startMonthTime = strtotime(date('Y-m', $startTimestamp));
        $endMonthTime = strtotime(date('Y-m', $endTimestamp));
        $arrKey = $startMonthTime . $endMonthTime;
        if (!isset($arr[$arrKey])) {
            $suffixArr = [];
            // 不允许查询连续超过12个月的数据
            $i = 1;
            while (true) {
                if ($startMonthTime > $endMonthTime) {
                    break;
                }
                if ($i > 12) {
                    throw_error_json('查询开始时间到截至时间，间隔不能超过12个月');
                }
                $suffixArr[] = date('Ym', $startMonthTime);
                $startMonthTime = strtotime('next month', $startMonthTime);
                $i++;
            }
            $arr[$arrKey] = $suffixArr;
        }
        return $arr[$arrKey];
    }

    /**
     * 获取列表构建器表单条件
     * @param array $filter
     * @return array
     */
    private function getListBuilderFilterWhere(array $filter): array {
        $ids = $filter['ids'];
        $where = [];
        // ids
        if ($ids) $where[] = ['id', 'in', $ids];
        // timeRange
        if ($startTime = $filter['timeRange']['start'] ?? '') $where[] = ['create_time', '>=', strtotime($startTime . ' 00:00:00')];
        if ($endTime = $filter['timeRange']['end'] ?? '') $where[] = ['create_time', '<=', strtotime($endTime . ' 23:59:59')];
        // memberIds
        if ($memberIds = $filter['memberIds']) $where[] = ['member_id', 'in', $memberIds];
        // dataId
        if ($dataId = $filter['dataId']) $where[] = ['data_id', '=', $dataId];
        // ipProvince
        if (($ipProvince = $filter['ipProvince']) && $ipProvince !== 'all') {
            if ($ipProvince === 'other') $ipProvince = '';
            $where[] = ['ip_province', '=', $ipProvince];
        }
        // fixedWhere
        $fixedWhereCall = $this->typeConfig['table']['fixed_where'] ?? null;
        if (is_callable($fixedWhereCall)) {
            $fixedWhere = call_user_func_array($fixedWhereCall, [$filter]);
            $where = array_merge($where, $fixedWhere);
        }

        return $where;
    }

    /**
     * 设置列表构建器cols表头权限
     * @param array $cols
     */
    private function setListBuilderColsAuth(array &$cols): void {
        foreach ($cols as $k => $v) {
            if (in_array($k, ['id'])) continue;
            $cols[$k]['auth'] = self::authKey($this->type, 'view_normal');
        }
    }

}